package com.cscj.android.repository

import androidx.room.Room
import com.cscj.android.repository.datastore.AppDataSourceImpl
import com.cscj.android.repository.datastore.AppDataStore
import com.cscj.android.repository.db.CacheDatabase
import com.cscj.android.repository.network.ResultFlowTransformer
import com.cscj.android.repository.network.api.service.RankService
import com.cscj.android.repository.network.api.service.ShortCutService
import com.cscj.android.repository.repo.config.WebConfigRepository
import com.cscj.android.repository.repo.config.WebConfigRepositoryImpl
import com.cscj.android.repository.repo.favorites.FavoritesRepository
import com.cscj.android.repository.repo.favorites.FavoritesRepositoryImpl
import com.cscj.android.repository.repo.history.BrowserHistoryRepository
import com.cscj.android.repository.repo.history.BrowserHistoryRepositoryImpl
import com.cscj.android.repository.repo.search.SearchRepository
import com.cscj.android.repository.repo.search.SearchRepositoryImpl
import com.cscj.android.repository.repo.shortcut.ShortCutRepository
import com.cscj.android.repository.repo.shortcut.ShortCutRepositoryImpl
import com.cscj.android.repository.settings.Settings
import com.cscj.android.repository.settings.impl.BrowserSettingImpl
import com.cscj.android.repository.settings.impl.SearchSettingImpl
import com.cscj.android.repository.settings.impl.SettingsImpl
import com.cscj.android.repository.settings.impl.UserAgentSettingImpl
import com.cscj.android.utils.LOG
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import okhttp3.ConnectionPool
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.koin.dsl.module
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

private val databaseModule = module {
    single {
        Room.databaseBuilder(get(), CacheDatabase::class.java, CacheDatabase.databaseName)
            .fallbackToDestructiveMigration().build()
    }
}

private val daoModule = module {
    single {
        get<CacheDatabase>().getSearchEngineDao()
    }

    single {
        get<CacheDatabase>().getUserAgentDao()
    }

    single {
        get<CacheDatabase>().getBrowserHistoryDao()
    }

    single {
        get<CacheDatabase>().getFavoritesDao()
    }

    single {
        get<CacheDatabase>().getSearchHistoryDao()
    }

    single {
        get<CacheDatabase>().getHotSearchDao()
    }

    single {
        get<CacheDatabase>().getShortCutDao()
    }

}

private val settingModule = module {
    single<Settings> { SettingsImpl() }
    single<Settings.SearchSetting> { SearchSettingImpl() }
    single<Settings.BrowserSetting> { BrowserSettingImpl(get()) }
    single<Settings.UserAgentSetting> { UserAgentSettingImpl() }
}

private val networkModule = module {
    single<Gson> {
        GsonBuilder().disableInnerClassSerialization().setDateFormat("yyyy-MM-dd HH:mm:ss")
            .setLenient().create()
    }

    single { ResultFlowTransformer() }

    //okhttp builder factory.
    factory<OkHttpClient.Builder> {
        val interceptors = getAll<Interceptor>() //in
        val builder = OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
            .addNetworkInterceptor(HttpLoggingInterceptor().apply {
                if (BuildConfig.DEBUG) {
                    setLevel(HttpLoggingInterceptor.Level.BODY)
                }
            }).connectionPool(ConnectionPool(10, 5, TimeUnit.MINUTES))

        interceptors.forEach {
            builder.addInterceptor(it)
        }
        builder
    }

    //okhttp client
    factory<OkHttpClient> {
        val builder = get<OkHttpClient.Builder>()
        builder.build()
    }

    //retrofit factory.
    factory {
        val host: String = getProperty("host")
        LOG.d("Network Retrofit base url is $host")
        Retrofit.Builder().baseUrl(host).client(get())
            .addConverterFactory(GsonConverterFactory.create(get())).build()
    }
}

private val dataStoreModule = module {
    single<AppDataStore> { AppDataSourceImpl(get()) }
}

private val serviceModule = module {
    single<ShortCutService> {
        get<Retrofit>().create(ShortCutService::class.java)
    }
    single<RankService> {
        get<Retrofit>().create(RankService::class.java)
    }
}

val repositoryModule = module {
    includes(
        databaseModule, daoModule, settingModule, networkModule, dataStoreModule, serviceModule
    )
    single<WebConfigRepository> { WebConfigRepositoryImpl() }
    single<SearchRepository> { SearchRepositoryImpl() }
    single<FavoritesRepository> { FavoritesRepositoryImpl() }
    single<BrowserHistoryRepository> { BrowserHistoryRepositoryImpl() }
    single<ShortCutRepository> { ShortCutRepositoryImpl() }
}
